use std::env::args;
use std::fs::File;
use std::io::{self, Read, Write};

mod Syntax;
use Syntax::expr::{
    self, BinaryExpression, GroupingExpression, LiteralExpression, SyntaxExpression,
    UnaryExpression,
};
use Syntax::interpreter::Interpreter;
use Syntax::lexical::LexicalScanner;
use Syntax::object::Object;
use Syntax::prettyPrintA::VisitorPrettyPrinter;
use Syntax::printer::VisitorPrinter;
use Syntax::syntaxResult::SyntaxResult;
use Syntax::text::TextSpan;
use Syntax::token::{SyntaxKind, SyntaxToken};

use crate::Syntax::parser::SyntaxParser;
use crate::Syntax::text::SourceLocation;

//https://github.com/terrajobst/minsk/blob/master/src/Minsk/CodeAnalysis/Syntax/Lexer.cs;

fn main11() {
    let expression = SyntaxExpression::Binary(BinaryExpression {
        left: Box::new(SyntaxExpression::Unary(UnaryExpression {
            operator: SyntaxToken::new(
                SyntaxKind::Minus,
                TextSpan::new(1, 1, "-".to_string()),
                SourceLocation { line: 0, column: 0 },
            ),
            right: Box::new(SyntaxExpression::Literal(LiteralExpression {
                value: Some(Object::Number(123.0)),
            })),
        })),
        operator: SyntaxToken::new(
            SyntaxKind::Slash,
            TextSpan::new(1, 1, "*".to_string()),
            SourceLocation { line: 0, column: 0 },
        ),
        right: Box::new(SyntaxExpression::Grouping(GroupingExpression {
            expression: Box::new(SyntaxExpression::Literal(LiteralExpression {
                value: Some(Object::Number(45.67)),
            })),
        })),
    });

    let printer = VisitorPrinter {};
    println!("{:?}", printer.print(&expression).unwrap());
}
fn main() {
    let mut args: Vec<String> = args().collect();

    let mut script = Script::new(false);

    match args.len() {
        0 | 1 => script.runPrompt(),
        2 => script.runFile(&args[1]),

        _ => {
            println!("Usage: mini-compiler [script]");
            std::process::exit(64);
        }
    }
}

struct Script {
    enable_pretty: bool,
    interpreter: Interpreter,
    pretty: VisitorPrettyPrinter,
}

#[allow(non_snake_case)]
impl Script {
    pub fn new(_enablepretty: bool) -> Self {
        Script {
            enable_pretty: _enablepretty,
            interpreter: Interpreter::new(),
            pretty: VisitorPrettyPrinter::new(),
        }
    }
    pub fn runFile(&self, path: &String) {
        let mut file = match File::open(path) {
            Err(str) => {
                panic!("Can't open file: {}", path);
            }
            Ok(pathFile) => pathFile,
        };
        let mut buffer = String::new();

        match file.read_to_string(&mut buffer) {
            Err(why) => {
                panic!("couldn't read {}: {:?}", path, why);
            }
            Ok(_) => {
                self.runSourceText(buffer);
            }
        }
    }

    pub fn runPrompt(&mut self) {
        loop {
            print!(">");
            io::stdout().flush().unwrap();

            let mut buffer = String::new();
            match io::stdin().read_line(&mut buffer) {
                Ok(_) => {
                    buffer = buffer.trim().to_string();

                    if buffer.eq("#exit") {
                        break;
                    }
                    if buffer.eq("#closetree") {
                        self.enable_pretty = false;
                        continue;
                    }
                    if buffer.eq("#opentree") {
                        self.enable_pretty = true;

                        continue;
                    }
                    self.runSourceText(buffer);
                }
                Err(why) => {
                    panic!("couldn't read: {:?}", why);
                    //break;
                }
            }
        }
        println!("app exit!\n");
    }

    fn runSourceText(&self, codeText: String) {
        // // token
        // let mut lexer = LexicalScanner::new(codeText.as_str());
        // while let Some(token) = lexer.nextToken() {
        //     if !matches!(token.kind, SyntaxKind::Whitespace | SyntaxKind::Comments){
        //         println!("{:?}", token);
        //     }
        //     //println!("{:?}", token);
        //     if matches!(token.kind, SyntaxKind::Unknown | SyntaxKind::Eof)
        //     {
        //         break;
        //     }
        // }
        if codeText.eq("@"){
            self.interpreter.PrintEnvironment();
            return;
        }
        let mut parse = SyntaxParser::fromInputCode(codeText.as_str());

        match parse.parse() {
            Ok(exprs) => {
                if !parse.isSuccess() {
                    return;
                }
                /// Evaluate;
                // print ;
                self.interpreter.interpret(&exprs);

                // pretty;
                if self.enable_pretty {
                    self.pretty.interpret(&exprs);
                }
            }

            Err(error) => {
                //eprintln!("Main<parse> Result : \n{:?}", error.to_string());
            }
        }
    }
}
